home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 November / november_2001.iso / Browsers / Netscape 6.1 / browser.xpi / bin / chrome / toolkit.jar / content / global / autocomplete.xml < prev    next >
Encoding:
Extensible Markup Language  |  2001-07-15  |  48.7 KB  |  1,368 lines

  1. <?xml version="1.0"?>
  2.  
  3. <!DOCTYPE window [
  4.   <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" >
  5.   %textcontextDTD;
  6. ]>
  7.  
  8. <bindings id="autocompleteBindings"
  9.           xmlns="http://www.mozilla.org/xbl"
  10.           xmlns:html="http://www.w3.org/1999/xhtml"
  11.           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  12.  
  13.   <binding id="autocomplete" display="xul:menu"
  14.            extends="chrome://global/content/bindings/textbox.xml#textbox">
  15.     <resources>
  16.       <stylesheet src="chrome://global/content/autocomplete.css"/>
  17.       <stylesheet src="chrome://global/skin/autocomplete.css"/>
  18.     </resources>
  19.  
  20.     <content>
  21.       <xul:box class="autocomplete-internal-box textbox-internal-box" flex="1">
  22.         <xul:box class="autocomplete-icon-box" autostretch="never" inherits="userAction">
  23.           <children includes="image">
  24.             <xul:image class="autocomplete-icon" allowevents="true"/>
  25.           </children>
  26.         </xul:box>
  27.  
  28.         <xul:box class="autocomplete-textbox-container" autostretch="never" flex="1" allowevents="true">
  29.           <html:input anonid="textbox" class="autocomplete-textbox textbox-input" flex="1"
  30.                       inherits="onfocus,onblur,value,type,maxlength,disabled,size,readonly,userAction"/>
  31.         </xul:box>
  32.                             
  33.         <xul:box class="autocomplete-history-button" inherits="open,hidden=hideHistory">
  34.           <xul:image class="autocomplete-history-image"/>
  35.         </xul:box>
  36.       </xul:box>
  37.  
  38.       <xul:popupset anonid="popupset" ignorekeys="true">
  39.         <xul:popup anonid="popup" class="autocomplete-result-popup" inherits="for=id,nomatch"/>
  40.       </xul:popupset>
  41.  
  42.       <children includes="menupopup"/>
  43.     </content>
  44.  
  45.     <implementation>
  46.  
  47.       <constructor><![CDATA[
  48.         // set default property values
  49.         this.userAction = "none";
  50.         this.noMatch = true;
  51.         this.ifSetAttribute("timeout", 50);
  52.         this.ifSetAttribute("maxrows", 5);
  53.         this.ifSetAttribute("autoFill", false);
  54.         this.ifSetAttribute("showPopup", true);
  55.         this.ifSetAttribute("flexPopup", true);
  56.         this.ifSetAttribute("alwaysOpenPopup", false);
  57.         this.ifSetAttribute("disableAutocomplete", false);
  58.         
  59.         // initialize the search sessions
  60.         this.searchSessions = this.getAttribute("searchSessions");
  61.         
  62.         // assure that the history popup flexes as well as the results popup
  63.         this.setAttribute("sizetopopup", this.flexPopup);
  64.         
  65.         var mps = this.getElementsByTagName("menupopup");
  66.         if (mps.length == 0)
  67.           this.ifSetAttribute("hideHistory", true);
  68.         
  69.         attr = this.getAttribute("ontextcommand");
  70.         if (attr) this.ontextcommand = new Function("userAction", attr);
  71.         attr = this.getAttribute("ontextrevert");
  72.         if (attr) this.ontextrevert = new Function(attr);
  73.         attr = this.getAttribute("oninit");
  74.         if (attr) this.oninit = new Function(attr);
  75.  
  76.         // hack to work around lack of bottom-up constructor calling
  77.         if ("initialize" in this.resultsPopup)
  78.           this.resultsPopup.initialize();
  79.         
  80.         this.fireInit();
  81.       ]]></constructor>
  82.  
  83.       <destructor><![CDATA[
  84.       ]]></destructor>
  85.       
  86.       <!-- =================== PUBLIC PROPERTIES =================== -->
  87.  
  88.       <property name="value"
  89.                 onset="this.ignoreInputEvent = true;
  90.                        this.mInputElt.value = val;
  91.                        this.ignoreInputEvent = false;
  92.                        return val;"
  93.                 onget="return this.mInputElt ? this.mInputElt.value : null;"/>
  94.   
  95.       <property name="focused"
  96.                 onget="return this.getAttribute('focused') == 'true';"/>
  97.  
  98.       <!-- space-delimited string of search session types to use -->
  99.       <property name="searchSessions" onget="return this.getAttribute('searchSessions')">
  100.         <setter><![CDATA[
  101.           val = val ? val : "";
  102.           var list = val.split(" ");
  103.           this.mSessions = {};
  104.           this.mListeners = {};
  105.           this.mLastResults = {};
  106.           
  107.           for (var i in list) {
  108.             var name = list[i];
  109.             if (name != "") {
  110.               var contractid = "@mozilla.org/autocompleteSession;1?type=" + name;
  111.               try {
  112.                 var session =
  113.                   Components.classes[contractid].getService(Components.interfaces.nsIAutoCompleteSession);
  114.               } catch (e) {
  115.                 dump("### ERROR - unable to create search session \"" + session + "\".\n");
  116.                 break;
  117.               }
  118.               this.mSessions[name] = session;
  119.               this.mListeners[name] = new (this.mAutoCompleteListener)(name);
  120.               ++this.sessionCount;
  121.             }
  122.           }
  123.         ]]></setter>
  124.       </property>
  125.  
  126.       <!-- the number of sessions currently in use -->
  127.       <property name="sessionCount">0</property>
  128.  
  129.       <!-- number of milliseconds after a keystroke before a search begins -->
  130.       <property name="timeout"
  131.                 onset="return this.setAttribute('timeout', val);"
  132.                 onget="var t = parseInt(this.getAttribute('timeout')); return t ? t : 0;"/>
  133.  
  134.       <!-- number of milliseconds after a keystroke before a search begins -->
  135.       <property name="maxRows"
  136.                 onset="return this.setAttribute('maxrows', val);"
  137.                 onget="var t = parseInt(this.getAttribute('maxrows')); return t ? t : 0;"/>
  138.  
  139.       <!-- option for filling the textbox with the best match while typing 
  140.            and selecting the difference -->
  141.       <property name="autoFill"
  142.                 onset="return this.setAttribute('autoFill', val);"
  143.                 onget="return this.getAttribute('autoFill') == 'true';"/>
  144.  
  145.       <!-- option to show the popup containing the results -->
  146.       <property name="showPopup"
  147.                 onset="return this.setAttribute('showPopup', val);"
  148.                 onget="return this.getAttribute('showPopup') == 'true';"/>
  149.  
  150.       <!-- option to keep the popup open while typing, even when there are no matches -->
  151.       <property name="alwaysOpenPopup"
  152.                 onset="return this.setAttribute('alwaysOpenPopup', val);"
  153.                 onget="return this.getAttribute('alwaysOpenPopup') == 'true';"/>
  154.  
  155.       <!-- option to size the popup to fit the width of the entire widget -->
  156.       <property name="flexPopup"
  157.                 onset="this.setAttribute('sizetopopup', val); return this.setAttribute('flexPopup', val);"
  158.                 onget="return this.getAttribute('flexPopup') == 'true';"/>
  159.  
  160.       <!-- option to turn off autocomplete -->
  161.       <property name="disableAutocomplete"
  162.                 onset="return this.setAttribute('disableAutocomplete', val);"
  163.                 onget="return this.getAttribute('disableAutocomplete') == 'true';"/>
  164.  
  165.       <!-- option to hide the history menubutton -->
  166.       <property name="hideHistory"
  167.                 onset="this.setAttribute('hideHistory', val);"
  168.                 onget="return this.getAttribute('hideHistory') == 'true';"/>
  169.       
  170.       <!-- state which indicates the current action being performed by the user.
  171.            Possible values are : none, typing, scrolling -->
  172.       <property name="userAction"
  173.                 onset="return this.setAttribute('userAction', val);"
  174.                 onget="return this.getAttribute('userAction');"/>
  175.       
  176.       <!-- state which indicates if the last search had no matches -->
  177.       <property name="noMatch"
  178.                 onset="return this.setAttribute('nomatch', val);"
  179.                 onget="return this.getAttribute('nomatch') == 'true';"/>
  180.  
  181.       <!-- state which indicates a search is currently happening -->
  182.       <property name="isSearching">false</property>
  183.  
  184.       <!-- state which indicates a search timeout is current waiting -->
  185.       <property name="isWaiting" 
  186.                 onget="return this.mAutoCompleteTimer != 0;"/>
  187.  
  188.       <!-- reference to the results popup element -->
  189.       <property name="resultsPopup"><![CDATA[
  190.         var elt = document.getAnonymousElementByAttribute(this, "anonid", "popup");
  191.         elt.__AUTOCOMPLETE_BOX__ = this;
  192.         elt;
  193.       ]]></property>
  194.  
  195.       <!-- =================== PRIVATE PROPERTIES =================== -->
  196.  
  197.       <property name="mSessions"/>
  198.       <property name="mListeners"/>
  199.       <property name="mLastResults"/>
  200.       <property name="mLastKeyCode"/>
  201.       <property name="mAutoCompleteTimer">0</property>
  202.       <property name="mMenuOpen">false</property>
  203.       <property name="mFinishAfterSearch">false</property>
  204.       <property name="mFireAfterSearch">false</property>
  205.       <property name="mNeedToFinish">false</property>
  206.       <property name="mNeedToComplete">false</property>
  207.       <property name="mView">null</property>
  208.       <property name="currentSearchString"/>
  209.       <property name="oninit"/>
  210.       <property name="ontextcommand"/>
  211.       <property name="ontextrevert"/>
  212.  
  213.       <property name="mAutoCompleteListener"><![CDATA[
  214.         var listener = function(aSession) { this.sessionName = aSession };
  215.         listener.prototype = {
  216.           param: this,
  217.           sessionName: null,
  218.           onAutoComplete: function(aResults, aStatus) 
  219.           {
  220.             this.param.processResults(this.sessionName, aResults, aStatus);
  221.           }
  222.         };
  223.         listener;
  224.       ]]></property>
  225.  
  226.       <property name="mPopupSetElt"><![CDATA[
  227.         var elt = document.getAnonymousElementByAttribute(this, "anonid", "popupset");
  228.         elt.__AUTOCOMPLETE_BOX__ = this;
  229.         elt;
  230.       ]]></property>
  231.  
  232.       <property name="mInputElt"><![CDATA[
  233.         document.getAnonymousElementByAttribute(this, "anonid", "textbox");
  234.       ]]></property>
  235.  
  236.       <!-- =================== PUBLIC METHODS =================== -->
  237.  
  238.       <!-- get the result object from the autocomplete results from a specific session -->
  239.       <method name="getResultAt">
  240.         <parameter name="aIndex"/>
  241.         <body><![CDATA[
  242.           var obj = this.convertIndexToSession(aIndex);
  243.           if (obj && this.mLastResults[obj.session]) {
  244.             var nsIAutoCompleteItem = Components.interfaces.nsIAutoCompleteItem;
  245.             if (obj.index >= 0) {
  246.               var item = this.mLastResults[obj.session].items.QueryElementAt(obj.index, nsIAutoCompleteItem);
  247.               return item;
  248.             }
  249.           }
  250.           return null;
  251.         ]]></body>
  252.       </method>
  253.  
  254.       <!-- get a value from the autocomplete results as a string via an absolute index-->
  255.       <method name="getResultValueAt">
  256.         <parameter name="aIndex"/>
  257.         <body><![CDATA[
  258.           var obj = this.convertIndexToSession(aIndex);
  259.           return obj ? this.getSessionValueAt(obj.session, obj.index) : null;
  260.         ]]></body>
  261.       </method>
  262.  
  263.       <!-- get the result object from the autocomplete results from a specific session -->
  264.       <method name="getSessionResultAt">
  265.         <parameter name="aSession"/>
  266.         <parameter name="aIndex"/>
  267.         <body><![CDATA[
  268.           var session = this.mLastResults[aSession];
  269.           if (session) {
  270.             var item = session.items.QueryElementAt(aIndex, Components.interfaces.nsIAutoCompleteItem);
  271.             return item;
  272.           }
  273.           return null;
  274.         ]]></body>
  275.       </method>
  276.  
  277.       <!-- get a value from the autocomplete results as a string from a specific session -->
  278.       <method name="getSessionValueAt">
  279.         <parameter name="aSession"/>
  280.         <parameter name="aIndex"/>
  281.         <body><![CDATA[
  282.           var result = this.getSessionResultAt(aSession, aIndex);
  283.           if (result)
  284.             return result.value;
  285.           return null;
  286.         ]]></body>
  287.       </method>
  288.  
  289.       <!-- get the total number of results in a specific session or overall if session is null-->
  290.       <method name="getResultCount">
  291.         <parameter name="aSession"/>
  292.         <body><![CDATA[
  293.           return this.view.rowCount;
  294.         ]]></body>
  295.       </method>
  296.       
  297.       <!-- get a session object by index -->
  298.       <method name="getSession">
  299.         <parameter name="aIndex"/>
  300.         <body><![CDATA[
  301.           var idx = 0;
  302.           for (var name in this.mSessions) {
  303.             if (idx == aIndex)
  304.               return this.mSessions[name];
  305.             ++idx;
  306.           }
  307.           return null;
  308.         ]]></body>
  309.       </method>
  310.  
  311.       <!-- get a session object by name -->
  312.       <method name="getSessionByName">
  313.         <parameter name="aSessionName"/>
  314.         <body><![CDATA[
  315.           return this.mSessions[aSessionName];
  316.         ]]></body>
  317.       </method>
  318.  
  319.       <!-- add a session by reference -->
  320.       <method name="addSession">
  321.         <parameter name="aSession"/>
  322.         <body><![CDATA[
  323.           ++this.sessionCount;
  324.           var name = "anon_"+this.sessionCount;
  325.           this.mSessions[name] = aSession;
  326.           this.mListeners[name] = new (this.mAutoCompleteListener)(name);
  327.         ]]></body>
  328.       </method>
  329.  
  330.       <!-- remove a session by reference -->
  331.       <method name="removeSession">
  332.         <parameter name="aSession"/>
  333.         <body><![CDATA[
  334.           for (var name in this.mSessions) {
  335.             if (this.mSessions[name] == aSession) {
  336.               delete this.mSessions[name];
  337.               delete this.mListeners[name];
  338.               --this.sessionCount;
  339.               break;
  340.             }
  341.           }
  342.         ]]></body>
  343.       </method>
  344.  
  345.       <!-- make this widget listen to all of the same autocomplete sessions 
  346.            from another autocomplete widget -->
  347.       <method name="syncSessions">
  348.         <parameter name="aCopyFrom"/>
  349.         <body><![CDATA[
  350.           this.sessionCount = aCopyFrom.sessionCount;
  351.           this.mSessions = {};
  352.           this.mListeners = {};
  353.           for (var name in aCopyFrom.mSessions) {
  354.             this.mSessions[name] = aCopyFrom.mSessions[name];
  355.             this.mListeners[name] = new (this.mAutoCompleteListener)(name);
  356.           }
  357.         ]]></body>
  358.       </method>
  359.  
  360.       <!-- get the first session that has results -->
  361.       <method name="getDefaultSession">
  362.         <body><![CDATA[
  363.           for (var name in this.mLastResults) {
  364.             var results = this.mLastResults[name];
  365.             if (results && results.items.Count() > 0)
  366.               return name;
  367.           }
  368.           return null;
  369.         ]]></body>
  370.       </method>
  371.  
  372.       <!-- empty the cached result data and empty the results popup -->
  373.       <method name="clearResults">
  374.         <parameter name="aInvalidate"/>
  375.         <body><![CDATA[
  376.           this.clearResultData();
  377.           this.clearResultElements(aInvalidate);
  378.         ]]></body>
  379.       </method>
  380.  
  381.       <!-- =================== PRIVATE METHODS =================== -->
  382.   
  383.       <!-- ::::::::::::: session searching ::::::::::::: -->
  384.  
  385.       <!--  -->
  386.       <method name="callListener">
  387.         <parameter name="me"/>
  388.         <parameter name="aAction"/>
  389.         <body><![CDATA[
  390.           // bail if the binding was detached or the element removed from
  391.           // document during the timeout
  392.           if (!("startLookup" in me) || !me.ownerDocument || !me.parentNode)
  393.             return;
  394.  
  395.           me.clearTimer();
  396.             
  397.           if (me.disableAutocomplete)
  398.             return;
  399.  
  400.           switch (aAction) {
  401.             case "startLookup":
  402.               me.startLookup();
  403.               break;
  404.  
  405.             case "stopLookup":
  406.               me.stopLookup();
  407.               break;
  408.  
  409.             case "autoComplete":
  410.               me.autoComplete();
  411.               break;
  412.           }
  413.         ]]></body>
  414.       </method>
  415.  
  416.       <!--  -->
  417.       <method name="startLookup">
  418.         <body><![CDATA[
  419.           var str = this.value;
  420.           this.lastSearchString = this.currentSearchString;
  421.           this.currentSearchString = str;
  422.           
  423.           this.isSearching = true;
  424.           this.mSessionReturns = this.sessionCount;
  425.           this.mFailureCount = 0;
  426.  
  427.           // tell each session to start searching...
  428.           for (var name in this.mSessions)
  429.             this.mSessions[name].onStartLookup(str, this.mLastResults[name], this.mListeners[name]);
  430.         ]]></body>
  431.       </method>
  432.  
  433.       <!--  -->
  434.       <method name="stopLookup">
  435.         <body><![CDATA[
  436.           for (var name in this.mSessions)
  437.             this.mSessions[name].onStopLookup();
  438.         ]]></body>
  439.       </method>
  440.  
  441.       <!--  -->
  442.       <method name="autoComplete">
  443.         <body><![CDATA[
  444.           for (var name in this.mSessions)
  445.             this.mSessions[name].onAutoComplete(this.value, 
  446.                                                 this.mLastResults[name],
  447.                                                 this.mListeners[name]);
  448.         ]]></body>
  449.       </method>
  450.  
  451.       <!--  -->
  452.       <method name="processResults">
  453.         <parameter name="aSessionName"/>
  454.         <parameter name="aResults"/>
  455.         <parameter name="aStatus"/>
  456.         <body><![CDATA[
  457.           if (this.disableAutocomplete)
  458.             return;
  459.  
  460.           --this.mSessionReturns;
  461.  
  462.           var firstReturn = this.mSessionReturns == (this.sessionCount-1) - this.mFailureCount;
  463.           
  464.           if (firstReturn)
  465.             this.clearResults(false); // clear results, but don't repaint yet
  466.           
  467.           // check the many criteria for failure
  468.           if (aStatus == Components.interfaces.nsIAutoCompleteStatus.failed ||
  469.               aStatus == Components.interfaces.nsIAutoCompleteStatus.ignored || 
  470.               aStatus == Components.interfaces.nsIAutoCompleteStatus.noMatch ||
  471.               aResults == null ||
  472.               aResults.items.Count() == 0 ||
  473.               aResults.searchString != this.currentSearchString)
  474.           {
  475.             this.mLastResults[aSessionName] = null;
  476.             this.searchFailed();
  477.             return;
  478.           }
  479.           
  480.           this.mLastResults[aSessionName] = aResults;
  481.  
  482.           // if this is the first session to return...
  483.           if (firstReturn) {
  484.             this.autoFillInput(aSessionName, aResults);
  485.             this.openResultPopup();
  486.           }
  487.          
  488.           // if this is the last session to return... 
  489.           if (this.mSessionReturns == 0) 
  490.             this.postSearchCleanup();
  491.           
  492.           this.addResultElements(aSessionName, aResults);            
  493.         ]]></body>
  494.       </method>
  495.  
  496.       <!-- called each time a search fails. If all searches have failed, 
  497.            clear the list and close the popup -->
  498.       <method name="searchFailed">
  499.         <body><![CDATA[
  500.           // if it's the last session to return, time to clean up...
  501.           if (this.mSessionReturns == 0)
  502.             this.postSearchCleanup();
  503.  
  504.           ++this.mFailureCount;
  505.           
  506.           // if all searches are done and they all failed...
  507.           if (this.mSessionReturns == 0 && this.mFailureCount == this.sessionCount) {
  508.             if (this.alwaysOpenPopup) {
  509.               this.clearResults(true); // clear data and repaint empty
  510.               
  511.               if (this.value) {
  512.                 this.openResultPopup();
  513.               } else
  514.                 this.closeResultPopup(); 
  515.             } else
  516.               this.closeResultPopup(); 
  517.           }
  518.         ]]></body>
  519.       </method>
  520.  
  521.       <!-- does some stuff after a search is done (success or failure) -->
  522.       <method name="postSearchCleanup">
  523.         <body><![CDATA[
  524.           this.isSearching = false;
  525.  
  526.           // figure out if there are no matches in all search sessions
  527.           var failed = true;
  528.           for (var name in this.mSessions) {
  529.             if (this.mLastResults[name])
  530.               failed = this.mLastResults[name].items.Count() < 1;
  531.             if (!failed)
  532.               break;
  533.           }
  534.           this.noMatch = failed;
  535.           
  536.           if (this.mFinishAfterSearch)
  537.             this.finishAutoComplete(this.mFireAfterSearch);
  538.         ]]></body>
  539.       </method>
  540.  
  541.       <!-- when the focus exits the widget or user hits return, 
  542.            determine what value to leave in the textbox -->
  543.       <method name="finishAutoComplete">
  544.         <parameter name="aFireTextCommand"/>
  545.         <body><![CDATA[
  546.           this.mFinishAfterSearch = false;
  547.           this.mFireAfterSearch = false;
  548.           if (this.mNeedToFinish && !this.disableAutocomplete) {
  549.             // if a search is happening at this juncture, bail out of this function
  550.             // and let the search finish, and tell it to come back here when it's done
  551.             if (this.isSearching || this.isWaiting) {
  552.               this.mFinishAfterSearch = true;
  553.               this.mFireAfterSearch = aFireTextCommand;
  554.               return;
  555.             }
  556.             
  557.             this.mNeedToFinish = false;
  558.  
  559.             // set textbox value to either override value, or default search result 
  560.             var val = this.resultsPopup.getOverrideValue();
  561.             if (val) {
  562.               this.value = val;
  563.             } else if (this.mNeedToComplete) {
  564.               // only fill in default value if there is nothing currently selected in popup
  565.               var defaultSession = this.getDefaultSession();
  566.               if (defaultSession) {
  567.                 var results = this.mLastResults[defaultSession];
  568.                 if (results && !this.noMatch && results.defaultItemIndex != -1)
  569.                   this.value = this.getSessionValueAt(defaultSession, results.defaultItemIndex);
  570.               }
  571.             }
  572.  
  573.             this.closeResultPopup();
  574.           }
  575.           
  576.           this.clearTimer();
  577.                     
  578.           this.currentSearchString = "";
  579.           this.mNeedToComplete = false;
  580.           
  581.           if (aFireTextCommand)
  582.             this.fireTextCommand(this.userAction);
  583.         ]]></body>
  584.       </method>
  585.  
  586.       <!--  when the user clicks an entry in the autocomplete popup -->
  587.       <method name="onResultClick">
  588.         <body><![CDATA[
  589.           // set textbox value to either override value, or the clicked result
  590.           var val = this.resultsPopup.getOverrideValue();
  591.           if (val)
  592.             this.value = val;
  593.           else if (this.resultsPopup.selectedIndex != null && !this.noMatch) {
  594.             this.value = this.getResultValueAt(this.resultsPopup.selectedIndex);
  595.           }
  596.  
  597.           this.mNeedToFinish = false;
  598.           this.mNeedToComplete = false;
  599.           
  600.           this.closeResultPopup();
  601.  
  602.           this.currentSearchString = "";
  603.  
  604.           this.fireTextCommand("clicking");
  605.         ]]></body>
  606.       </method>
  607.  
  608.       <!-- when the user hits escape, revert the previously typed value in the textbox -->
  609.       <method name="undoAutoComplete">
  610.         <body><![CDATA[
  611.           var val = this.currentSearchString;
  612.  
  613.           var ok = this.fireTextRevert();
  614.           if ((ok || ok == undefined) && val)
  615.             this.value = val;
  616.  
  617.           this.userAction = "typing";
  618.  
  619.           this.currentSearchString = "";
  620.           this.mNeedToComplete = false;
  621.         ]]></body>
  622.       </method>
  623.  
  624.       <!-- convert an absolute result index into a session name/index pair -->
  625.       <method name="convertIndexToSession">
  626.         <parameter name="aIndex"/>
  627.         <body><![CDATA[
  628.           var idx = 0;
  629.           for (var name in this.mLastResults) {
  630.             if (this.mLastResults[name]) {
  631.               if ((idx+this.mLastResults[name].items.Count())-1 >= aIndex) {
  632.                 return {session: name, index: aIndex-idx};
  633.               }
  634.               idx += this.mLastResults[name].items.Count();
  635.             }
  636.           }
  637.           return null;
  638.         ]]></body>
  639.       </method>
  640.  
  641.       <!-- ::::::::::::: user input handling ::::::::::::: -->
  642.  
  643.       <!--  -->
  644.       <method name="processInput">
  645.         <body><![CDATA[
  646.           // stop current lookup in case it's async.
  647.           this.stopLookup();
  648.           // stop the queued up lookup on a timer
  649.           this.clearTimer();
  650.  
  651.           if (this.ignoreInputEvent)
  652.             return;
  653.           
  654.           if (this.disableAutocomplete)
  655.             return;
  656.  
  657.           this.userAction = "typing";
  658.           this.mNeedToFinish = true;
  659.           this.mNeedToComplete = true;
  660.  
  661.           this.resultsPopup.selectedIndex = null;
  662.           
  663.           // We want to autocomplete only if the user is editing at the end of the text
  664.           if (this.mInputElt.selectionEnd >= this.value.length)
  665.             this.mAutoCompleteTimer = setTimeout(this.callListener, this.timeout, this, "startLookup");
  666.           else
  667.             this.noMatch = true;
  668.         ]]></body>
  669.       </method>
  670.  
  671.       <!--  -->
  672.       <method name="processKeyPress">
  673.         <parameter name="aEvent"/>
  674.         <body><![CDATA[
  675.           this.mLastKeyCode = aEvent.keyCode;
  676.           
  677.           var killEvent = false;
  678.           
  679.           switch (aEvent.keyCode) {
  680.             case KeyEvent.DOM_VK_TAB:
  681.               killEvent = this.mMenuOpen;
  682.               if (killEvent)
  683.                 this.keyNavigation(aEvent);
  684.               break;              
  685.               
  686.             case KeyEvent.DOM_VK_RETURN:
  687.               killEvent = this.mMenuOpen;
  688.               this.finishAutoComplete(true);
  689.               break;
  690.  
  691.             case KeyEvent.DOM_VK_ESCAPE:
  692.               this.clearTimer();
  693.  
  694.               killEvent = this.mMenuOpen;
  695.               this.undoAutoComplete();
  696.               this.closeResultPopup();
  697.               break;
  698.   
  699.             case KeyEvent.DOM_VK_LEFT:
  700.             case KeyEvent.DOM_VK_RIGHT:
  701.               this.clearTimer();
  702.  
  703.               this.closeResultPopup();
  704.               break;
  705.  
  706.             case KeyEvent.DOM_VK_PAGE_UP:
  707.             case KeyEvent.DOM_VK_PAGE_DOWN:
  708.             case KeyEvent.DOM_VK_UP:
  709.             case KeyEvent.DOM_VK_DOWN:
  710.               this.clearTimer();
  711.               
  712.               killEvent = true;
  713.               this.keyNavigation(aEvent);
  714.               break;
  715.           }
  716.           
  717.           if (killEvent) {
  718.             aEvent.preventDefault();
  719.             aEvent.preventBubble();
  720.           }
  721.           
  722.           return true;
  723.         ]]></body>
  724.       </method>
  725.  
  726.       <!--  -->
  727.       <method name="keyNavigation">
  728.         <parameter name="aEvent"/>
  729.         <body><![CDATA[
  730.           var k = aEvent.keyCode;
  731.           if (k == KeyEvent.DOM_VK_TAB ||
  732.               k == KeyEvent.DOM_VK_UP || k == KeyEvent.DOM_VK_DOWN ||
  733.               k == KeyEvent.DOM_VK_PAGE_UP || k == KeyEvent.DOM_VK_PAGE_DOWN)
  734.           {
  735.             // up/down keys while menu is closed will open menu
  736.             if (!this.mMenuOpen && (this.view.rowCount > 0 || this.alwaysOpenPopup)) {
  737.               this.openResultPopup();
  738.               return;
  739.             }
  740.             
  741.             this.userAction = "scrolling";
  742.             this.mNeedToComplete = false;
  743.             
  744.             var dir = k == KeyEvent.DOM_VK_DOWN ||
  745.                       k == KeyEvent.DOM_VK_PAGE_DOWN ||
  746.                      (k == KeyEvent.DOM_VK_TAB && !aEvent.shiftKey) ? 1 : -1;
  747.             var amt = k == KeyEvent.DOM_VK_PAGE_UP ||
  748.                       k == KeyEvent.DOM_VK_PAGE_DOWN ? this.resultsPopup.pageCount-1 : 1;
  749.             var selected = this.resultsPopup.selectBy(dir, amt);
  750.           
  751.             // determine which value to place in the textbox
  752.             this.ignoreInputEvent = true;
  753.             if (selected != null) {
  754.               this.value = this.getResultValueAt(selected);
  755.             } else {
  756.               this.value = this.currentSearchString;
  757.             }
  758.             
  759.             // move cursor to the end
  760.             this.mInputElt.setSelectionRange(this.value.length, this.value.length);
  761.             this.ignoreInputEvent = false;
  762.           }
  763.         ]]></body>
  764.       </method>
  765.  
  766.       <!-- while the user is typing, fill the textbox with the "default" value
  767.            if one can be assumed, and select the end of the text -->
  768.       <method name="autoFillInput">
  769.         <parameter name="aSessionName"/>
  770.         <parameter name="aResults"/>
  771.         <body><![CDATA[
  772.           if (!aSessionName || aSessionName != this.getDefaultSession())
  773.             return;
  774.             
  775.           if (!this.mFinishAfterSearch && this.autoFill && this.mLastKeyCode != 8) {
  776.             if (aResults.defaultItemIndex != -1) {
  777.               var resultValue = this.getSessionValueAt(aSessionName, aResults.defaultItemIndex);
  778.               var match = resultValue.toLowerCase();
  779.               var entry = this.currentSearchString.toLowerCase();
  780.               this.ignoreInputEvent = true;
  781.               if (match.indexOf(entry) == 0) {
  782.                 var endPoint = this.value.length;
  783.                 this.value = this.value + resultValue.substr(endPoint);
  784.                 this.mInputElt.setSelectionRange(endPoint, this.value.length);
  785.               } else {
  786.                 this.value = this.value + " >> " + resultValue;
  787.                 this.mNeedToComplete = true;
  788.                 this.mInputElt.setSelectionRange(entry.length, this.value.length);
  789.               }
  790.               this.ignoreInputEvent = false;
  791.             }
  792.           } 
  793.         ]]></body>
  794.       </method>
  795.  
  796.       <!-- ::::::::::::: popup and outliner ::::::::::::: -->
  797.  
  798.       <!--  -->
  799.       <method name="openResultPopup">
  800.         <body><![CDATA[
  801.           if (!this.mMenuOpen && this.showPopup && this.focused) {
  802.             if (this.flexPopup) {
  803.               var w = this.boxObject.width;
  804.               if (w != this.resultsPopup.boxObject.width)
  805.                 this.resultsPopup.setAttribute("width", w);
  806.             }
  807.             
  808.             //dump("open popup\n");
  809.             this.resultsPopup.openPopup(this, -1, -1, "popup", "bottomleft", "topleft");
  810.             this.mMenuOpen = true;
  811.           }
  812.         ]]></body>
  813.       </method>
  814.  
  815.       <!--  -->
  816.       <method name="closeResultPopup">
  817.         <body><![CDATA[
  818.           if (this.resultsPopup && this.mMenuOpen) {
  819.             //dump("close popup\n");
  820.             //var boxObject = this.mPopupSetElt.boxObject.QueryInterface(Components.interfaces.nsIPopupSetBoxObject);
  821.             //boxObject.hidePopup();
  822.             this.resultsPopup.closePopup();
  823.             this.mMenuOpen = false;
  824.           }
  825.         ]]></body>
  826.       </method>
  827.  
  828.       <!--  -->
  829.       <method name="addResultElements">
  830.         <parameter name="aSessionName"/>
  831.         <parameter name="aResults"/>
  832.         <body><![CDATA[
  833.           if (this.focused) {
  834.             this.view.addResults(aResults);
  835.             this.resultsPopup.adjustHeight();
  836.           }
  837.         ]]></body>
  838.       </method>
  839.  
  840.       <!--  -->
  841.       <method name="clearResultElements">
  842.         <parameter name="aInvalidate"/>
  843.         <body><![CDATA[
  844.           this.view.clearResults(aInvalidate);
  845.           if (aInvalidate)
  846.             this.resultsPopup.adjustHeight();
  847.  
  848.           this.noMatch = true;
  849.         ]]></body>
  850.       </method>
  851.  
  852.       <!--  -->
  853.       <method name="clearResultData">
  854.         <body><![CDATA[
  855.           for (var name in this.mSessions)
  856.             this.mLastResults[name] = null;
  857.         ]]></body>
  858.       </method>
  859.  
  860.       <!-- ::::::::::::: miscellaneous ::::::::::::: -->
  861.  
  862.       <!--  -->
  863.       <method name="ifSetAttribute">
  864.         <parameter name="aAttr"/>
  865.         <parameter name="aVal"/>
  866.         <body><![CDATA[
  867.           if (this.getAttribute(aAttr) == "")
  868.             this.setAttribute(aAttr, aVal);
  869.         ]]></body>
  870.       </method>
  871.  
  872.       <!--  -->
  873.       <method name="clearTimer">
  874.         <parameter name=""/>
  875.         <body><![CDATA[
  876.           if (this.mAutoCompleteTimer) {
  877.             clearTimeout(this.mAutoCompleteTimer);
  878.             this.mAutoCompleteTimer = 0;
  879.           }
  880.         ]]></body>
  881.       </method>
  882.  
  883.       <!-- ::::::::::::: event dispatching ::::::::::::: -->
  884.  
  885.      <!-- execute the external init handler -->
  886.       <method name="fireInit">
  887.         <body><![CDATA[
  888.           return this.oninit ? this.oninit() : null;
  889.         ]]></body>
  890.       </method>
  891.  
  892.       <!-- execute the external command handler -->
  893.       <method name="fireTextCommand">
  894.         <parameter name="aUserAction"/>
  895.         <body><![CDATA[
  896.           return this.ontextcommand ? this.ontextcommand(aUserAction) : null;
  897.         ]]></body>
  898.       </method>
  899.  
  900.      <!-- execute the external command handler -->
  901.       <method name="fireTextRevert">
  902.         <body><![CDATA[
  903.           return this.ontextrevert ? this.ontextrevert() : null;
  904.         ]]></body>
  905.       </method>
  906.  
  907.       <!-- =================== OUTLINER VIEW =================== -->
  908.  
  909.       <property name="view"><![CDATA[
  910.         ({
  911.           mTextbox: this,
  912.           mOutliner: null,
  913.           mBoxObject: null,
  914.           mResults: [],
  915.           mRowCount: 0,
  916.           mSelectedIndex: null,
  917.           
  918.           get outlinerBoxObject()
  919.           {
  920.             return this.mOutliner;
  921.           },
  922.           
  923.           set selectedIndex(aRow)
  924.           {
  925.             if (!this.mBoxObject)
  926.               return;
  927.               
  928.             if (this.mSelectedIndex != null)
  929.               this.mBoxObject.invalidateRow(this.mSelectedIndex);
  930.               
  931.             this.mSelectedIndex = aRow;
  932.             
  933.             this.mBoxObject.invalidateRow(aRow);
  934.             
  935.             if (aRow != null)
  936.               this.mBoxObject.ensureRowIsVisible(aRow);
  937.           },
  938.           
  939.           get selectedIndex()
  940.           {
  941.             return this.mSelectedIndex;
  942.           },
  943.           
  944.           clearResults: function(aInvalidate)
  945.           {
  946.             //dump("clearResults\n");
  947.             this.mRowCount = 0;  
  948.             this.mResults = [];
  949.             
  950.             if (aInvalidate && this.mOutliner)
  951.               this.mOutliner.invalidate();
  952.           },
  953.           
  954.           addResults: function(aResults)
  955.           {
  956.             //dump("addResults\n");
  957.             this.mResults.push(aResults);
  958.             var oldCount = this.mRowCount;
  959.             this.mRowCount += aResults.items.Count();
  960.             
  961.             if (this.mOutliner)
  962.               this.mOutliner.rowCountChanged(oldCount, this.mRowCount);
  963.           },
  964.           
  965.           createAtom: function(aVal)
  966.           {
  967.             try {
  968.               var i = Components.interfaces.nsIAtomService;
  969.               var svc = Components.classes["@mozilla.org/atom-service;1"].getService(i);
  970.               return svc.getAtom(aVal);
  971.             } catch(ex) { }
  972.           },
  973.  
  974.           //////////////////////////////////////////////////////////
  975.           // nsIOutlinerView interface
  976.           
  977.           get rowCount() {
  978.             return this.mRowCount;
  979.           },
  980.           
  981.           // implementing these results in a crash
  982.           // get selection() {},
  983.           // set selection(aVal) { },
  984.           
  985.           setOutliner: function(aOutliner)
  986.           {
  987.             //dump(this.mOutliner + "//" + aOutliner + "\n");
  988.             this.mOutliner = aOutliner;
  989.             
  990.             if (aOutliner) {
  991.               this.mBoxObject = this.mTextbox.resultsPopup.outliner.outlinerBoxObject;
  992.             
  993.               // cache atoms for pseudoelement properties
  994.               this.mAtomSelected = this.createAtom("menuactive")
  995.             }
  996.           },
  997.           
  998.           getCellText: function(aRow, aColId)
  999.           {
  1000.             //if (aRow == 0 && aColId == "value") dump("START PAINT "+aRow+"\n");
  1001.             var result = this.mTextbox.getResultAt(aRow);
  1002.             if (!result) return "";
  1003.             return aColId == "value" ? result.value : (aColId == "comment" ? result.comment : "");
  1004.           },
  1005.  
  1006.           getRowProperties: function(aIndex, aProperties) 
  1007.           {
  1008.             if (aIndex == this.mSelectedIndex)
  1009.               aProperties.AppendElement(this.mAtomSelected);
  1010.           },
  1011.  
  1012.           getCellProperties: function(aIndex, aColId, aProperties)
  1013.           {
  1014.             this.getRowProperties(aIndex, aProperties);
  1015.           },
  1016.  
  1017.           getColumnProperties: function(aColId, aColElt, aProperties) 
  1018.           {
  1019.           },
  1020.           
  1021.           getParentIndex: function(aRowIndex) { },
  1022.           hasNextSibling: function(aRowIndex, aAfterIndex) { },
  1023.           getLevel: function(aIndex) {},
  1024.           isContainer: function(aIndex) {},
  1025.           isContainerOpen: function(aIndex) {},
  1026.           isContainerEmpty: function(aIndex) {},
  1027.           toggleOpenState: function(aIndex) {},
  1028.           selectionChanged: function() {},
  1029.           cycleHeader: function(aColId, aElt) {},
  1030.           cycleCell: function(aRow, aColId) {},
  1031.           isEditable: function(aRow, aColId) {},
  1032.           setCellText: function(aRow, aColId, aValue) {},
  1033.           performAction: function(aAction) {},
  1034.           performActionOnRow: function(aAction, aRow) {},
  1035.           performActionOnCell: function(aAction, aRow, aColId) {}
  1036.         });
  1037.       ]]></property>
  1038.  
  1039.     </implementation>
  1040.  
  1041.     <handlers>
  1042.       <handler event="input"
  1043.                action="this.processInput();"/>
  1044.  
  1045.       <handler event="keypress" phase="capturing"
  1046.                 action="return this.processKeyPress(event);"/>
  1047.  
  1048.       <handler event="focus" phase="capturing"
  1049.                action="this.userAction = 'typing';"/>
  1050.  
  1051.       <handler event="blur" phase="capturing"
  1052.                action="this.userAction = 'none'; this.finishAutoComplete(false);"/>
  1053.     </handlers>
  1054.   </binding> 
  1055.  
  1056.   <binding id="autocomplete-result-popup" extends="chrome://global/content/bindings/popup.xml#popup">
  1057.     <resources>
  1058.       <stylesheet src="chrome://global/content/autocomplete.css"/>
  1059.       <stylesheet src="chrome://global/skin/autocomplete.css"/>
  1060.     </resources>
  1061.  
  1062.     <content>
  1063.       <xul:box class="autocomplete-result-box" flex="1">
  1064.         <xul:outliner anonid="outliner" class="autocomplete-outliner" flex="1">
  1065.           <xul:outlinerbody anonid="outlinerbody" class="autocomplete-outlinerbody" flex="1"/>
  1066.         </xul:outliner>
  1067.       </xul:box>
  1068.     </content>
  1069.     
  1070.     <implementation>
  1071.       <constructor><![CDATA[
  1072.         if (this.textbox && this.textbox.view)
  1073.           this.initialize();
  1074.       ]]></constructor>
  1075.  
  1076.      <property name="textbox"
  1077.                 onget="return this.__AUTOCOMPLETE_BOX__;"/>
  1078.       
  1079.       <property name="outliner">
  1080.         document.getAnonymousElementByAttribute(this, "anonid", "outliner");
  1081.       </property>
  1082.  
  1083.       <property name="outlinerbody">
  1084.         document.getAnonymousElementByAttribute(this, "anonid", "outlinerbody");
  1085.       </property>
  1086.  
  1087.       <property name="view" onget="return this.mView;">
  1088.         <setter><![CDATA[
  1089.           this.mView = val;
  1090.           var bx = this.outliner.boxObject;
  1091.           bx = bx.QueryInterface(Components.interfaces.nsIOutlinerBoxObject);
  1092.           bx.view = val;
  1093.         ]]></setter>
  1094.       </property>
  1095.  
  1096.      <property name="pageCount"
  1097.                onget="return this.outliner.outlinerBoxObject.getPageCount();"/>
  1098.  
  1099.       <property name="selectedIndex"
  1100.                 onget="return this.textbox.view.selectedIndex"
  1101.                 onset="this.textbox.view.selectedIndex = val; return val;"/>
  1102.  
  1103.      <property name="mLastRows">0</property>
  1104.  
  1105.       <method name="initialize">
  1106.         <body><![CDATA[
  1107.         this.outliner.__AUTOCOMPLETE_BOX__ = this.textbox;
  1108.         this.outlinerbody.__AUTOCOMPLETE_BOX__ = this.textbox;
  1109.  
  1110.         this._selectedIndex = null;
  1111.  
  1112.         this.initColumns();
  1113.         this.view = this.textbox.view;
  1114.         ]]></body>
  1115.       </method>
  1116.  
  1117.       <!-- initialize the columns in the outliner -->
  1118.       <method name="initColumns">
  1119.         <body><![CDATA[
  1120.           this.addColumn({id: "value", flex: 1});
  1121.         ]]></body>
  1122.       </method>
  1123.  
  1124.       <method name="addColumn">
  1125.         <parameter name="aAttrs"/>
  1126.         <body><![CDATA[
  1127.           var col = document.createElement("outlinercol");
  1128.           col.setAttribute("class", "autocomplete-outlinercol");
  1129.           for (var name in aAttrs)
  1130.             col.setAttribute(name, aAttrs[name]);
  1131.           this.outliner.appendChild(col);
  1132.           return col;
  1133.         ]]></body>
  1134.       </method>
  1135.  
  1136.       <method name="adjustHeight">
  1137.         <body><![CDATA[
  1138.           // detect the desired height of the outliner
  1139.           var bx = this.outliner.outlinerBoxObject;
  1140.           var view = this.textbox.view;
  1141.           var rows = this.textbox.maxRows;
  1142.           if (!view.rowCount || (rows && view.rowCount < rows))
  1143.             rows = view.rowCount;
  1144.           
  1145.           var height = rows * bx.rowHeight;
  1146.           
  1147.           this.outliner.setAttribute("height", height);
  1148.           this.outliner.setAttribute("hidescrollbar", view.rowCount <= rows);
  1149.         ]]></body>
  1150.       </method>
  1151.  
  1152.       <method name="selectBy">
  1153.         <parameter name="aDir"/>
  1154.         <parameter name="aAmount"/>
  1155.         <body><![CDATA[
  1156.           try {
  1157.             var bx = this.outliner.outlinerBoxObject;
  1158.             var view = bx.view;
  1159.             this.selectedIndex = this.getNextIndex(aDir, aAmount, this.selectedIndex, view.rowCount-1);
  1160.   
  1161.             return this.selectedIndex;
  1162.           } catch (ex) {
  1163.             // do nothing - occasionally timer-related js errors happen here
  1164.             // e.g. "this.selectedIndex has no properties", when you type fast and hit a
  1165.             // navigation key before this popup has opened
  1166.           }
  1167.         ]]></body>
  1168.       </method>
  1169.  
  1170.       <method name="getNextIndex">
  1171.         <parameter name="aDir"/>
  1172.         <parameter name="aAmount"/>
  1173.         <parameter name="aIndex"/>
  1174.         <parameter name="aMaxRow"/>
  1175.         <body><![CDATA[
  1176.           if (aMaxRow < 0)
  1177.             return null;
  1178.             
  1179.           var newIdx = aIndex + aDir*aAmount;
  1180.           if (aDir < 0 && aIndex == null || newIdx > aMaxRow && aIndex != aMaxRow)
  1181.             newIdx = aMaxRow;
  1182.           else if (aDir > 0 && aIndex == null || newIdx < 0 && aIndex != 0)
  1183.             newIdx = 0;
  1184.           
  1185.           if (newIdx < 0 && aIndex == 0 || newIdx > aMaxRow && aIndex == aMaxRow)
  1186.             aIndex = null;
  1187.           else
  1188.             aIndex = newIdx;
  1189.           
  1190.           return aIndex;
  1191.         ]]></body>
  1192.       </method>
  1193.  
  1194.       <!-- This method is meant to be overriden by bindings extending this one.  When the 
  1195.            user selects an item from the list by hitting enter or clicking, this method
  1196.            can set the value of the textbox to a different value if it wants to. -->
  1197.       <method name="getOverrideValue">
  1198.         <body><![CDATA[
  1199.           return null;
  1200.         ]]></body>
  1201.       </method>
  1202.  
  1203.     </implementation>
  1204.  
  1205.     <handlers>
  1206.       <handler event="create">
  1207.         this.textbox.mMenuOpen = true;
  1208.       </handler>
  1209.  
  1210.       <handler event="destroy">
  1211.         this.textbox.mMenuOpen = false;
  1212.         this.selectedIndex = null;
  1213.       </handler>
  1214.     </handlers>
  1215.   </binding>
  1216.  
  1217.   <binding id="autocomplete-outliner" extends="chrome://global/content/bindings/outliner.xml#outliner">
  1218.     <resources>
  1219.       <stylesheet src="chrome://global/content/autocomplete.css"/>
  1220.       <stylesheet src="chrome://global/skin/autocomplete.css"/>
  1221.     </resources>
  1222.  
  1223.     <content orient="vertical">
  1224.       <xul:box class="autocomplete-outliner-cols">
  1225.         <children includes="outlinercol"/>
  1226.       </xul:box>
  1227.       <xul:outlinerrows class="autocomplete-outlinerrows outliner-rows" inherits="hidescrollbar" flex="1">
  1228.         <children/>
  1229.       </xul:outlinerrows>
  1230.     </content>
  1231.  
  1232.     <implementation>
  1233.       <property name="textbox"
  1234.                 onget="return this.__AUTOCOMPLETE_BOX__;"/>
  1235.     </implementation>
  1236.   </binding>
  1237.  
  1238.   <binding id="autocomplete-outlinerbody">
  1239.     <resources>
  1240.       <stylesheet src="chrome://global/content/autocomplete.css"/>
  1241.       <stylesheet src="chrome://global/skin/autocomplete.css"/>
  1242.     </resources>
  1243.  
  1244.     <implementation>
  1245.       <property name="textbox"
  1246.                 onget="return this.__AUTOCOMPLETE_BOX__;"/>
  1247.  
  1248.       <property name="mLastMoveTime">new Date()</property>
  1249.  
  1250.       <method name="getHoverCell">
  1251.         <parameter name="aEvent"/>
  1252.         <body><![CDATA[
  1253.            var obox = this.textbox.view.outlinerBoxObject;
  1254.            var pbox = this.textbox.resultsPopup.boxObject;
  1255.  
  1256.            var row = {}; var col = {}; var obj = {};
  1257.            var x = aEvent.screenX-pbox.screenX + this.boxObject.x;
  1258.            var y = aEvent.screenY-pbox.screenY + this.boxObject.y;
  1259.            obox.getCellAt(x-1, y-1, row, col, obj);
  1260.            return {row: row.value, column: col.value};
  1261.         ]]></body>
  1262.       </method>
  1263.     </implementation>
  1264.     
  1265.     <handlers>
  1266.       <handler event="mouseout" action="this.textbox.view.selectedIndex = null;"/>
  1267.       <handler event="mouseup" action="this.textbox.onResultClick();"/>
  1268.  
  1269.       <handler event="mousemove"><![CDATA[
  1270.         if (new Date() - this.mLastMoveTime > 30) {
  1271.           var rc = this.getHoverCell(event);
  1272.           if (rc.row != this.textbox.view.selectedIndex)
  1273.             this.textbox.view.selectedIndex = rc.row;
  1274.           this.mLastMoveTime = new Date();
  1275.         }
  1276.       ]]></handler>
  1277.     </handlers>
  1278.   </binding>
  1279.  
  1280.   <binding id="autocomplete-outlinerrows" extends="xul:box">
  1281.     <resources>
  1282.       <stylesheet src="chrome://global/content/autocomplete.css"/>
  1283.       <stylesheet src="chrome://global/skin/autocomplete.css"/>
  1284.     </resources>
  1285.  
  1286.     <content onmousedown="event.preventDefault()">
  1287.       <xul:box flex="1" class="outliner-bodybox">
  1288.         <children/>
  1289.       </xul:box>
  1290.       <xul:scrollbar inherits="hidescrollbar" align="vertical" class="outliner-scrollbar"/>
  1291.     </content>
  1292.   </binding>
  1293.  
  1294.   <binding id="autocomplete-outlinercol">
  1295.     <resources>
  1296.       <stylesheet src="chrome://global/content/autocomplete.css"/>
  1297.       <stylesheet src="chrome://global/skin/autocomplete.css"/>
  1298.     </resources>
  1299.   </binding>
  1300.  
  1301.   <binding id="autocomplete-history-popup" extends="chrome://global/content/bindings/popup.xml#popup">
  1302.     <resources>
  1303.       <stylesheet src="chrome://global/content/autocomplete.css"/>
  1304.       <stylesheet src="chrome://global/skin/autocomplete.css"/>
  1305.     </resources>
  1306.  
  1307.     <content>
  1308.       <xul:box class="autocomplete-result-box autocomplete-history-box" orient="vertical" flex="1">
  1309.         <children/>
  1310.       </xul:box>
  1311.     </content>
  1312.   </binding>
  1313.  
  1314.   <binding id="autocomplete-internal-box">
  1315.     <content context="_child">
  1316.       <children/>
  1317.       <xul:popupset>
  1318.         <xul:popup oncreate="this.parentNode.parentNode.doPopupItemEnabling(this);">
  1319.           <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"
  1320.                         oncommand="var controller = document.commandDispatcher.getControllerForCommand('cmd_undo');              
  1321.                                    controller.doCommand('cmd_undo');"/>
  1322.           <xul:menuseparator/>
  1323.           <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"
  1324.                         oncommand="var controller = document.commandDispatcher.getControllerForCommand('cmd_cut');
  1325.                                    controller.doCommand('cmd_cut');"/>
  1326.           <xul:menuitem label="©Cmd.label;" accesskey="©Cmd.accesskey;" cmd="cmd_copy"
  1327.                         oncommand="var controller = document.commandDispatcher.getControllerForCommand('cmd_copy');
  1328.                                    controller.doCommand('cmd_copy');"/>
  1329.           <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"
  1330.                         oncommand="var controller = document.commandDispatcher.getControllerForCommand('cmd_paste');
  1331.                         controller.doCommand('cmd_paste');"/>
  1332.           <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"
  1333.                         oncommand="var controller = document.commandDispatcher.getControllerForCommand('cmd_delete');
  1334.                                    controller.doCommand('cmd_delete');"/>
  1335.           <xul:menuseparator/>
  1336.           <xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" command="cmd_selectAll"
  1337.                         oncommand="var controller = document.commandDispatcher.getControllerForCommand('cmd_selectAll');
  1338.                                    controller.doCommand('cmd_selectAll');"/>
  1339.         </xul:popup>
  1340.       </xul:popupset>
  1341.     </content>
  1342.  
  1343.     <implementation>
  1344.       <method name="doPopupItemEnabling">
  1345.         <parameter name="popupNode"/> 
  1346.         <body>
  1347.           <![CDATA[
  1348.             var children = popupNode.childNodes;
  1349.             for (var i = 0; i < children.length; i++) {              
  1350.               var command = children[i].getAttribute("cmd");
  1351.               if (command) {
  1352.                 var controller = document.commandDispatcher.getControllerForCommand(command);
  1353.                 var enabled = controller.isCommandEnabled(command);
  1354.                 if (enabled)
  1355.                   children[i].removeAttribute("disabled");
  1356.                 else
  1357.                   children[i].setAttribute("disabled", "true");               
  1358.               }
  1359.             }
  1360.           ]]>
  1361.         </body>
  1362.       </method>
  1363.     </implementation>
  1364.   </binding>
  1365.   
  1366. </bindings>
  1367.  
  1368.